// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package flate

// Sort sorts data.
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func sortByFreq( []literalNode) {
	 := len()
	quickSortByFreq(, 0, , maxDepth())
}

func quickSortByFreq( []literalNode, , ,  int) {
	for - > 12 { // Use ShellSort for slices <= 12 elements
		if  == 0 {
			heapSort(, , )
			return
		}
		--
		,  := doPivotByFreq(, , )
		// Avoiding recursion on the larger subproblem guarantees
		// a stack depth of at most lg(b-a).
		if - < - {
			(, , , )
			 =  // i.e., quickSortByFreq(data, mhi, b)
		} else {
			(, , , )
			 =  // i.e., quickSortByFreq(data, a, mlo)
		}
	}
	if - > 1 {
		// Do ShellSort pass with gap 6
		// It could be written in this simplified form cause b-a <= 12
		for  :=  + 6;  < ; ++ {
			if [].freq == [-6].freq && [].literal < [-6].literal || [].freq < [-6].freq {
				[], [-6] = [-6], []
			}
		}
		insertionSortByFreq(, , )
	}
}

func doPivotByFreq( []literalNode, ,  int) (,  int) {
	 := int(uint(+) >> 1) // Written like this to avoid integer overflow.
	if - > 40 {
		// Tukey's ``Ninther,'' median of three medians of three.
		 := ( - ) / 8
		medianOfThreeSortByFreq(, , +, +2*)
		medianOfThreeSortByFreq(, , -, +)
		medianOfThreeSortByFreq(, -1, -1-, -1-2*)
	}
	medianOfThreeSortByFreq(, , , -1)

	// Invariants are:
	//	data[lo] = pivot (set up by ChoosePivot)
	//	data[lo < i < a] < pivot
	//	data[a <= i < b] <= pivot
	//	data[b <= i < c] unexamined
	//	data[c <= i < hi-1] > pivot
	//	data[hi-1] >= pivot
	 := 
	,  := +1, -1

	for ;  <  && ([].freq == [].freq && [].literal < [].literal || [].freq < [].freq); ++ {
	}
	 := 
	for {
		for ;  <  && ([].freq == [].freq && [].literal > [].literal || [].freq > [].freq); ++ { // data[b] <= pivot
		}
		for ;  <  && ([].freq == [-1].freq && [].literal < [-1].literal || [].freq < [-1].freq); -- { // data[c-1] > pivot
		}
		if  >=  {
			break
		}
		// data[b] > pivot; data[c-1] <= pivot
		[], [-1] = [-1], []
		++
		--
	}
	// If hi-c<3 then there are duplicates (by property of median of nine).
	// Let's be a bit more conservative, and set border to 5.
	 := - < 5
	if ! && - < (-)/4 {
		// Lets test some points for equality to pivot
		 := 0
		if [].freq == [-1].freq && [].literal > [-1].literal || [].freq > [-1].freq { // data[hi-1] = pivot
			[], [-1] = [-1], []
			++
			++
		}
		if [-1].freq == [].freq && [-1].literal > [].literal || [-1].freq > [].freq { // data[b-1] = pivot
			--
			++
		}
		// m-lo = (hi-lo)/2 > 6
		// b-lo > (hi-lo)*3/4-1 > 8
		// ==> m < b ==> data[m] <= pivot
		if [].freq == [].freq && [].literal > [].literal || [].freq > [].freq { // data[m] = pivot
			[], [-1] = [-1], []
			--
			++
		}
		// if at least 2 points are equal to pivot, assume skewed distribution
		 =  > 1
	}
	if  {
		// Protect against a lot of duplicates
		// Add invariant:
		//	data[a <= i < b] unexamined
		//	data[b <= i < c] = pivot
		for {
			for ;  <  && ([-1].freq == [].freq && [-1].literal > [].literal || [-1].freq > [].freq); -- { // data[b] == pivot
			}
			for ;  <  && ([].freq == [].freq && [].literal < [].literal || [].freq < [].freq); ++ { // data[a] < pivot
			}
			if  >=  {
				break
			}
			// data[a] == pivot; data[b-1] < pivot
			[], [-1] = [-1], []
			++
			--
		}
	}
	// Swap pivot into middle
	[], [-1] = [-1], []
	return  - 1, 
}

// Insertion sort
func insertionSortByFreq( []literalNode, ,  int) {
	for  :=  + 1;  < ; ++ {
		for  := ;  >  && ([].freq == [-1].freq && [].literal < [-1].literal || [].freq < [-1].freq); -- {
			[], [-1] = [-1], []
		}
	}
}

// quickSortByFreq, loosely following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993.

// medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
func medianOfThreeSortByFreq( []literalNode, , ,  int) {
	// sort 3 elements
	if [].freq == [].freq && [].literal < [].literal || [].freq < [].freq {
		[], [] = [], []
	}
	// data[m0] <= data[m1]
	if [].freq == [].freq && [].literal < [].literal || [].freq < [].freq {
		[], [] = [], []
		// data[m0] <= data[m2] && data[m1] < data[m2]
		if [].freq == [].freq && [].literal < [].literal || [].freq < [].freq {
			[], [] = [], []
		}
	}
	// now data[m0] <= data[m1] <= data[m2]
}